#pragma once
#include "VertexIndexData.h"
#include <vector>
#include <map>
struct IDirect3DVertexBuffer9;
struct IDirect3DIndexBuffer9;
class Mesh;

typedef std::vector<WORD> IndexMap;

// copy from Ogre
enum VertexElementSemantic {
	VES_POSITION = 1,
	VES_BLEND_WEIGHTS = 2,
    VES_BLEND_INDICES = 3,
	VES_NORMAL = 4,
	VES_DIFFUSE = 5,
	VES_SPECULAR = 6,
	VES_TEXTURE_COORDINATES = 7,
    VES_BINORMAL = 8,
    VES_TANGENT = 9
};

enum VertexElementType
{
    VET_FLOAT1 = 0,
    VET_FLOAT2 = 1,
    VET_FLOAT3 = 2,
    VET_FLOAT4 = 3,
	VET_COLOUR = 4,
	VET_SHORT1 = 5,
	VET_SHORT2 = 6,
	VET_SHORT3 = 7,
	VET_SHORT4 = 8,
    VET_UBYTE4 = 9,
    VET_COLOUR_ARGB = 10,
    VET_COLOUR_ABGR = 11
};

struct VertexDeclare {
	WORD source, type, semantic, offset, index;
};
typedef std::vector<VertexDeclare> VertexDeclareList;

struct VertexDataBuff {
	DWORD VertexCount;
	WORD BindIndex;
	WORD VertexSize;
	VertexDeclareList VertexDeclares;
	std::vector<char> Buff;
};

struct IndexDataBuff {
	DWORD IndexCount;
	bool Uint32Index;
	std::vector<char> Buff;
};

struct VertexBoneAssignment {
	DWORD VertexIndex;
	WORD BoneIndex;
	float Weight;
};

typedef std::multimap<size_t, VertexBoneAssignment> VertexBoneAssignmentList;

struct SubMeshData {
	std::string Name;
	std::string Material;
	bool UseSharedGeometry;
	WORD OperationType;
	IndexDataBuff IndexBuff;
	VertexDataBuff PrivateVertexBuff;
	VertexBoneAssignmentList VertexBoneAssignments;
};
typedef std::vector<SubMeshData> SubMeshDataList;

struct MeshData {
	std::string Name;
	std::string Skeleton;
	bool SkeletallyAnimated;
	AxisAlignedBox Bounds;
	float BoundRadius;
	VertexDataBuff SharedVertexBuff;
	VertexBoneAssignmentList VertexBoneAssignments;
	SubMeshDataList SubMeshes;
};

//
// MeshBuilder
//
class MeshBuilder
{
public:
	struct Vertex
	{
		Vector3 Position;
		Vector3 Normal;
		Vector3 Tangent;
		Vector2 Texcoord;
		WORD InfluenceBones[MAX_INFLUENCES];
		float InfluenceWeights[MAX_INFLUENCES];

		Vertex() {}
		Vertex(const Vector3& position, const Vector3& normal, const Vector3& tangent, const Vector2& texcoord)
			: Position(position), Normal(normal), Tangent(tangent), Texcoord(texcoord) {}
	};
	typedef std::vector<Vertex> VertexList;
	typedef std::vector<unsigned short> IndexList;

	void Reset();
	void Begin(const std::string& name, size_t reserve = 0);
	void AddVertex(const Vector3& position, const Vector2& texcoord, const Vector3& normal, const Vector3& tangent = Vector3::Zero);
	void AddVertex(const Vertex& vertex);
	void AddTriangle(unsigned short v0, unsigned short v1, unsigned short v2);
	void End();

	bool BuildMesh(MeshData& meshData, Mesh* mesh);

	Mesh* getMesh() { return mMesh; }

public:
	MeshBuilder() {}
	~MeshBuilder() {}
	
private:
	VertexList mBuildingList;
	IndexList mIndexList;
	Mesh* mMesh;
	AxisAlignedBox mAABB;

	bool ProcessVertexBuffer(
		const VertexDataBuff& vb, 
		const VertexBoneAssignmentList& vbas, 
		IndexMap& indexMap);

	VertexData::StreamInfo BuildPositionVB(bool gpuSkin = false);
	VertexData::StreamInfo BuildNoPositionVB();
	IDirect3DIndexBuffer9* BuildIndexBuffer();

	IDirect3DVertexBuffer9* CreateVB(size_t size, size_t usage, size_t pool);
	IDirect3DIndexBuffer9* CreateIB(size_t size, size_t usage, size_t pool);
};
